/***********************************************************************************
Copy right:	    2022-2030 .
Author:         Dev_XH
Date:           2022.1.26
Description:    《基于N32G457VEL7的远程环境监测报警系统的设计与实现》
***********************************************************************************/

/* 头文件 */
#include "board.h"
#include "cJSON.h"


/* 宏定义 */
#define MAX_REPORT_BUF  128     //上报缓冲区最大字节数
#define MAX_DISCONNECT  5       //断连次数

/* 全局变量 */
static rt_thread_t poll_thread = RT_NULL;       // 定义 poll_thread 线程控制块指针
static rt_thread_t key_thread = RT_NULL;        // 定义 key_thread 线程控制块指针
static rt_thread_t report_thread = RT_NULL;     // 定义 report_thread 线程控制块指针
u8 ESP8266_INIT_OK = 0;                         // esp8266初始化完成标志
DHT11_Data_TypeDef dht11 = {0};                 //温湿度
char esp8266_send_buf[MAX_REPORT_BUF] = {0};    // esp8266发送缓冲区
rt_uint32_t poll_time = 3000;                   // 采集时间 默认3秒
rt_uint32_t report_time = 5000;                 // 采集时间 默认5秒
int max_humidity = 70;                          // 最大湿度值 默认70
int min_humidity = 10;                          // 最小湿度值 默认10
int max_temperature = 34;                       // 最大温度值 默认35
int min_temperature = 15;                       // 最小温度值 默认15
float max_light = 700.0;                        // 最大光照度值 默认700.0
float min_light = 10.0;                         // 最小光照度值 默认10.0
char alarm_flag = 0;                            // 报警标志位
char wifi_err_flag = 0;                         // wifi连接错误标志位

/* 函数声明 */
static void poll_thread_entry(void* parameter);
static void key_thread_entry(void* parameter);
static void report_thread_entry(void* parameter);

/* 主函数 */
int main(void)
{
    /* 创建 key 按键线程 */
    key_thread = rt_thread_create( "key", key_thread_entry,RT_NULL, 256, 14, 20);
     /* 启动 key 按键线程 */
     if (key_thread != RT_NULL)
    {
        rt_thread_startup(key_thread);
        rt_kprintf("\ncreate [ key_thread ] success! \n");
    }else
    {
        rt_kprintf("\ncreate [ key_thread ] failed! \n");
        return RT_ERROR;
    }

    /* 创建 poll 采集线程 */
    poll_thread = rt_thread_create("poll", poll_thread_entry, RT_NULL, 1024, 10, 20);
    /* 启动 poll 采集线程 */
    if (poll_thread != RT_NULL)
    {
        rt_thread_startup(poll_thread);
        rt_kprintf("\ncreate [ poll_thread ] success! \n");
    }else
    {
        rt_kprintf("\ncreate [ poll_thread ] failed! \n");
        return RT_ERROR;
    }

    /* 创建 report 上报线程 */
    report_thread = rt_thread_create("report", report_thread_entry, RT_NULL, 1024, 10, 20);
    /* 启动 report 上报线程 */
    if (report_thread != RT_NULL)
    {
        rt_thread_startup(report_thread);
        rt_kprintf("\ncreate [ report_thread ] success! \n");
    }else
    {
        rt_kprintf("\ncreate [ report_thread ] failed! \n");
        return RT_ERROR;
    }
    
    return RT_EOK;
}

/**
 * @brief:	按键线程函数。
 * @param:	parameter: 线程传参。
 * @return:	void。
 */
static void key_thread_entry(void* parameter)
{
    while(1)
    {
        /* K1被按下 */
        if(Key_Scan(KEY1_INPUT_PORT,KEY1_INPUT_PIN) == KEY_ON)
        {
            rt_kprintf(" >>>   [KEY1] is push!\n");
            rt_hw_led_on(3);
            BEEP(ON);
        }else if (Key_Scan(KEY2_INPUT_PORT,KEY2_INPUT_PIN) == KEY_ON)
        {
            rt_kprintf(" >>>   [KEY2] is push!\n");
            rt_hw_led_off(3);
            BEEP(OFF);
        }else if (Key_Scan(KEY3_INPUT_PORT,KEY3_INPUT_PIN) == KEY_ON)
        {
            rt_kprintf(" >>>   [KEY3] is push!\n");
        }

        while (alarm_flag != 0)
        {
            rt_hw_led_on(2);
            BEEP_ON;
            rt_thread_delay(200);
            rt_hw_led_off(2);
            BEEP_OFF;
            rt_thread_delay(200);
        }

        led_run(1,500);
    }
}

/**
 * @brief:	采集线程函数。
 * @param:	parameter: 线程传参。
 * @return:	void。
 */
static void poll_thread_entry(void* parameter)
{
    float light = 0.0;              //光照度
    char light_str[10] = {0};       //光照数据转字符串缓冲区
    unsigned char oled_buf[21] = {0};        //OLED显示缓冲区
    char str_buf[11] = {0};         //数据转字符串缓冲区

    /* OLED屏幕初始化 */
	OLED_Init();
	OLED_ColorTurn(0);//0正常显示，1 反色显示
    OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
    OLED_Refresh_Line("OLED");
    rt_kprintf("[OLED] init success! \n");

    /* 温湿度传感器初始化初始化 */
    dht11_init();
    OLED_Refresh_Line("DHT11");
    rt_kprintf("[DHT11] init success! \n");

    /* 光照度传感器初始化初始化 */
    if (BH1750_Init() == 0)
    {
        OLED_Refresh_Line("BH1750");
        rt_kprintf("\n < BH1750_Init success! > \n");
    }else
    {
        rt_kprintf("\n < BH1750_Init failed! > \n");
    }

    OLED_Clear();

    while(1)
    {
        /********** 温湿度传感器获取数据**************/
        if(Read_DHT11(&dht11) == SUCCESS)
        {
            //rt_kprintf("\r\nTem:%d.%d RH  Hum: %d.%d C \r\n",dht11.humi_int,dht11.humi_deci,dht11.temp_int,dht11.temp_deci);
        }

        /********** 光照度传感器获取数据**************/
        if (!i2c_CheckDevice(BH1750_Addr))
        {
            light = LIght_Intensity();
            snprintf(light_str, 9, "%.1f", light);
            //rt_kprintf("\r\nlight:%s Lx \r\n",light_str);
        }else
        {
            rt_kprintf("BH1750 device not find ! \n");
        }

        if (dht11.humi_int == 0 || dht11.temp_int == 0)    //等数据采集成功再显示
        {
            rt_thread_delay(200);
            continue;
        }

        if (wifi_err_flag == 1)     //wifi连接成功显示
        {
            snprintf((char *)oled_buf, 20, "<Welcome>");
            OLED_ClearAndShowString(32,0,oled_buf,16);
        }else   //wifi连接错误显示
        {
            snprintf((char *)oled_buf, 20, "WIFI state ERR!");
            OLED_ClearAndShowString(0,0,oled_buf,16);
        }

        snprintf((char *)oled_buf, 20, "Hum:%d.%d RH",dht11.humi_int,dht11.humi_deci);
        OLED_ClearAndShowString(0,16,oled_buf,16);
        
        snprintf((char *)oled_buf, 20, "Temp:%d.%d C",dht11.temp_int,dht11.temp_deci);
        OLED_ClearAndShowString(0,32,oled_buf,16);

        snprintf((char *)oled_buf, 20, "Light:%.1f Lx",light);
        OLED_ClearAndShowString(0,48,oled_buf,16);
        OLED_Refresh();

        /* 报警逻辑判断 */
        if ((dht11.humi_int<min_humidity || dht11.humi_int>max_humidity) || (dht11.temp_int<min_temperature || dht11.temp_int>max_temperature) || (light<min_light || light>max_light))
        {
            alarm_flag = 1;     //报警标志位置位
        }else
        {
            alarm_flag = 0;     //报警标志位复位
        }

        cJSON *root = NULL;             //CJSON根节点
        root = cJSON_CreateObject();  //创建根数据对象
        snprintf(str_buf, 10, "%d.%d", dht11.humi_int,dht11.humi_deci);
        cJSON_AddStringToObject(root, "Humidity", str_buf);

        snprintf(str_buf, 10, "%d.%d", dht11.temp_int,dht11.temp_deci);
        cJSON_AddStringToObject(root, "Temperature", str_buf);

        snprintf(str_buf, 10, "%.1f", light);
        cJSON_AddStringToObject(root, "Light",str_buf);
        cJSON_AddNumberToObject(root, "Alarm_flag",alarm_flag);

        char *out = cJSON_PrintUnformatted(root);           //将json形式打印成无格式字符串形式

        strncpy(esp8266_send_buf, out, MAX_REPORT_BUF-1);   //拷贝到esp8266发送缓冲区中

        cJSON_free(out);        //释放内存
        if (root)
        {
            cJSON_Delete(root); //释放cJSON根节点内存
        }

        rt_thread_delay(poll_time);
    }
}

/**
 * @brief:	上报线程函数。
 * @param:	parameter: 线程传参。
 * @return:	void。
 */
static void report_thread_entry(void* parameter)
{
    char count = 0;
    wifi_info_t wifi = 
    {
        .wifi_name = "dev*****",
        .wifi_psw = "abc******",
        .wifi_ip = "118.***.**.**",
        .wifi_port = "****"
    };
    //wifi_info_t* wifi_ptr = &wifi;
    //uint8_t *read_ptr = NULL;

    /* 从flash读取wifi数据 */
    //read_ptr = (uint8_t*)PAGE_ADDR(62);
    //memcpy(wifi_ptr, read_ptr, sizeof(wifi));

    ESP8266_Init(&wifi); //初始化ESP8266
    rt_kprintf("[ESP8266] init success! \n");

    while (1)
    {
        if (ESP8266_connect_status() != 0)
        {
            if (rt_strnlen(esp8266_send_buf,200) != 0)  //判断是否是空包
            {
                ESP8266_SendData((unsigned char *)esp8266_send_buf, rt_strnlen(esp8266_send_buf,200));  //将数据上报到第三方服务器
            }
            wifi_err_flag = 1;
        }else
        {
            wifi_err_flag = 0;
            count++;
            rt_kprintf("WIFI connect status err!\n");

            if (count >= MAX_DISCONNECT)
            {
                ESP8266_Init(&wifi); //初始化ESP8266
                count = 0;
            }
        }
        
        rt_thread_delay(report_time);
    }
}

/**
 * @brief:	设置时间参数函数。
 * @param:	传参。
 * @return:	void。
 */
void set_time_para(int argc, char **argv)
{
    rt_uint32_t time = 0;

    time = atoi(argv[2]);
    rt_kprintf("time_str:<%s>, time_int:<%d> \n", argv[2], time);

    if (argc == 3)
    {
        if (rt_strcmp(argv[1], "poll") == 0)
        {
            if (100 <= time && time <= 3600000)    //采集时间设置范围：[100,3600000]毫秒 => [100毫秒,1小时]
            {
                poll_time = time;
                rt_kprintf("set poll_time is [%d] success! \n", poll_time);
            }else
            {
                rt_kprintf(" Parameter range error! poll_time range is [1000,3600000] ! \n");
            }
        }else if (rt_strcmp(argv[1], "report") == 0)
        {
            if (1000 <= time && time <= 3600000)    //采集时间设置范围：[1,3600]秒 => [1秒,1小时]
            {
                report_time = time;
                rt_kprintf("set report_time is [%d] success! \n", report_time);
            }else
            {
                rt_kprintf("Parameter range error! report_time range is [1000,3600000] ! \n");
            }
        }else
        {
            rt_kprintf("Instruction error or incorrect parameter ! \n");
        }
    }else
    {
        rt_kprintf("Instruction error or incorrect parameter ! \n");
    }
}
MSH_CMD_EXPORT(set_time_para, set_time_para sample);

/**
 * @brief:	设置采集参数阈值函数。
 * @param:	传参。
 * @return:	void。
 */
void set_para(int argc, char **argv)
{
    int num_char = 0;
    float num_float = 0.0;

    num_char = atoi(argv[3]);
    rt_kprintf("num_str:<%s>, num_int:<%d> \n", argv[3], num_char);

    num_float = atof(argv[3]);
    printf("num_str:<%s>, num_int:<%.1f> \n", argv[3], num_float);

    if (argc == 4)
    {
        if (rt_strcmp(argv[1], "max") == 0)
        {
            if (rt_strcmp(argv[2], "hum") == 0)
            {
                if (1<=num_char && num_char<=70)
                {
                    max_humidity = num_char;
                }else
                {
                    rt_kprintf("Parameter range error! humidity range is [1,70]!\n");
                }
            }else if (rt_strcmp(argv[2], "tem") == 0)
            {
                if (-10<=num_char && num_char<=40)
                {
                    max_temperature = num_char;
                }else
                {
                    rt_kprintf("Parameter range error! temperature range is [-10,40]!\n");
                }
            }else if (rt_strcmp(argv[2], "lig") == 0)
            {
                if (1.0<=num_float && num_float<=1000.0)
                {
                    max_light = num_float;
                }else
                {
                    rt_kprintf("Parameter range error! humidity light is [1.0,1000]!\n");
                }
            }
        }else if (rt_strcmp(argv[1], "min") == 0)
        {
            if (rt_strcmp(argv[2], "hum") == 0)
            {
                if (1<=num_char && num_char<=70)
                {
                    min_humidity = num_char;
                }else
                {
                    rt_kprintf("Parameter range error! humidity range is [1,70]!\n");
                }
            }else if (rt_strcmp(argv[2], "tem") == 0)
            {
                if (-10<=num_char && num_char<=40)
                {
                    min_temperature = num_char;
                }else
                {
                    rt_kprintf("Parameter range error! temperature range is [-10,40]!\n");
                }
            }else if (rt_strcmp(argv[2], "lig") == 0)
            {
                if (1.0<=num_float && num_float<=1000)
                {
                    min_light = num_float;
                }else
                {
                    rt_kprintf("Parameter range error! humidity light is [1.0,1000]!\n");
                }
            }
        }else
        {
            rt_kprintf("Instruction error or incorrect parameter ! \n");
        }
    }else
    {
        rt_kprintf("Instruction error or incorrect parameter ! \n");
    }
}
MSH_CMD_EXPORT(set_para, set_para sample);

/**
 * @brief:	打印当前采集参数阈值函数。
 * @param:	传参。
 * @return:	void。
 */
void print_para_range(void)
{
    rt_kprintf("\n/********************************************************************/\n\n");
    rt_kprintf("     min_humidity:[%d]             max_humidity[%d]\n", min_humidity, max_humidity);
    rt_kprintf("     min_temperature:[%d]          max_temperature[%d]\n", min_temperature, max_temperature);
    printf("     min_light:[%.1f]              max_light[%.1f]\n", min_light, max_light);
    rt_kprintf("\n/********************************************************************/\n");
}
MSH_CMD_EXPORT(print_para_range, print_para_range sample);

